{--

Represents the game rules.
Evaluates the game etc.

--}
module Environment.Game where 



import Environment.Representation_benni as R
--import Representation_benni as R
import Data.Vector  as V 

g = initGame

getApp::Action->Int
getApp (app,_,_)   = app

getCard::Action->Card
getCard (_,card,_)  = card

getSlot::Action->Int
getSlot (_,_,slot)  = slot

getLife::Life->Int
getLife (Life l) = l

isAlive::Environment->Int->Bool 
isAlive e i = life > 0
            where life = getLife $ fst $ e!i


getVitality::Environment->Int->Int
getVitality e i = getLife $ fst $  e!i

getSlotI::Int->Environment->Slot
getSlotI i e = e V.! i                 

-- Ermittelt den Index des Feldes dass die hoechsten Leben hat und I als Funktion haellt
getFreeFieldWithLife::Environment->Int
getFreeFieldWithLife e = if V.null e 
                                then -1
                                else getFreeFieldWithLife' i (i+1) max e
                         where (i,max) = getLifeOfFirstI e 0 
                                
--find the first Card with I in Field                      
getLifeOfFirstI::Environment->Int->(Int,Int) 
getLifeOfFirstI e i = if  (V.length e) >= i 
                              then (-1,-1)
                              else 
                                if (snd $ e!i) == (VCard I) 
                                        then (i,getLife $ fst $ e!i)
                                        else getLifeOfFirstI e (i+1)
                              
-- the real computation                                 
getFreeFieldWithLife'::Int-> -- index of maxLife
                       Int-> --running integer!
                       Int-> --max Life
                       Environment->Int
getFreeFieldWithLife' (-1) _ (-1) _ = -1 --error state!
getFreeFieldWithLife' imax i max e = if 255 < i 
                                        then imax 
                                        else 
                                             if max < life 
                                                then getFreeFieldWithLife' i (i+1) life e
                                                else getFreeFieldWithLife' imax (i+1) max e
                                     where life = getLife $ fst $ e!i
                                                                
updateGame::Action -> Bool -> Game -> Game
--proponen's turn
updateGame a True g = Game{prop=newProp,op=newOp,turns=(turn+1)}
                      where 
                          g' = zombieEval True g
                          prop = R.prop g'
                          op = R.op g'
                          turn = R.turns g'
                          (newProp,newOp) = updateEnvironment False prop op a
--opponent's turn                          
updateGame a False g = Game{prop=newProp,op=newOp,turns=(turn+1)}
                       where 
                          g' = zombieEval False g
                          prop = R.prop g'
                          op = R.op g'
                          turn = R.turns g'
                          (newOp,newProp) = updateEnvironment False op prop a
                            
--evalutation of all zombies
zombieEval::Bool->              --which player's turn?
            Game->
            Game
--prop's turn            
zombieEval True g = Game{prop=prop',op=op',turns=turn}
                   where prop = R.prop g
                         op = R.op g
                         turn = R.turns g
                         zombieList = getAllZombies prop 0    
                         (prop',op')=zombieEval' zombieList prop op
--op's turn                         
zombieEval False g = Game{prop=prop',op=op',turns=turn}
                     where prop = R.prop g
                           op = R.op g
                           turn = R.turns g
                           zombieList = getAllZombies op 0    
                           (op',prop') = zombieEval' zombieList op prop
                           
zombieEval'::[Int]->    --Indeces of all zombies of one player
             Environment->
             Environment->
             (Environment,Environment)
zombieEval' [] prop op = (prop, op)
zombieEval' (i:is) prop op = zombieEval' is prop'' op'
                           where action= (2,I,i)
                                 (prop',op') = updateEnvironment True prop op action
                                 prop'' = unsafeUpd prop' [(i,(mkLife 0,VCard I))]
                         
--returns all indecies of zombiesolts                           
getAllZombies::Environment->Int->[Int]
getAllZombies e 256 = []
getAllZombies e i = if life == -1
                       then  i:getAllZombies e (i+1)
                       else getAllZombies e (i+1)
                    where Life life = fst $ e V.! i               


updateEnvironment::Bool->                               -- is Zombie?
                   Environment->                        -- prop Environment
                   Environment->                        -- Op Environment 
                   Action->                             -- Action performed
                   (Environment,Environment)            -- Updated Environments for one Hand
updateEnvironment isZombie prop op a = if getApp a == 1
                                                then eval isZombie i (VApp card field) prop op
                                                else eval isZombie i (VApp field card) prop op
                                where card = if (getCard a) == Zero
                                                then (VInt 0)
                                                else (VCard  $ getCard a)
                                      i = getSlot a
                                      field = snd $ prop V.! i
                                      
-- card to slot evaluation                                     
eval::Bool->
               Int->
               Field->
               Environment->
               Environment->
               (Environment,Environment)
eval isZombie i app prop op = realEval isZombie i app prop op 1000


inEnv:: Int -> Bool
inEnv i = (i >= 0) && (i < 258)

---general Eval function
realEval::Bool->                        --isZombie
      Int->                             --Field to evaluat
      Field->                           --Slotindex to current slot
      Environment->                     --prop
      Environment->                     --op
      Int->                             --Interations
      (Environment,Environment)
realEval _ i _ prop op 0                                = (V.unsafeUpd prop [(i,(fst $ prop V.!i,VCard I))],op)      
realEval _ i (VApp (VCard I) a) prop op _               = apply iden i a prop op  -- check
realEval _ i (VApp a (VCard Zero)) prop op _            = apply zero i a prop op  
realEval _ i (VApp (VCard Succ) a) prop op _            = apply succ' i a prop op  -- check
realEval _ i (VApp (VCard Dbl) a) prop op _             = apply dbl i a prop op -- check
realEval isZombie i (VApp (VCard Dec) a) prop op _      = dec isZombie i a prop op --check
realEval isZombie i (VApp (VCard Inc) a) prop op _      = inc isZombie i a prop op --check
realEval _ i (VApp (VCard Get) a) prop op _             = get i a prop op --check
realEval _ i (VApp (VCard Put) a) prop op _             = apply put i a prop op -- check
realEval _ i (VApp (VCard Revive) a) prop op _          = revive i a prop op -- check
realEval _ i (VApp (VCard Copy) a) prop op _            = copy' i a prop op --check
realEval _ i (VApp (VApp (VCard Zombie) j) a) prop op _                      = zombie i j prop op  --check                
realEval _ i (VApp (VApp (VCard K) x) y) prop op _                           = k x i prop op --check
realEval isZombie i (VApp (VApp (VApp (VCard S) f) g) a) prop op iter        = s isZombie f g a i prop op iter
realEval isZombie i (VApp (VApp (VApp (VCard Attack) f) g) a) prop op _      = attack isZombie f g a i prop op 
realEval isZombie i (VApp (VApp (VApp (VCard Help) f) g) a) prop op _        = help isZombie f g a i prop op
realEval _ i (VApp (VInt _)_) prop op _ = (V.unsafeUpd prop [(i,(fst $ prop V.!i,VCard I))],op) -- error state somthing went wrong
realEval _ i field prop op _ = (V.unsafeUpd prop [(i,(fst $ prop V.!i,field))],op) -- Card is not jet valide
 
--apply function as higher oder thingi
apply::(Field->Slot->Slot)->            --function 
        Int->                           --Slot number
        Field->                         --perhaps needed
        Environment->                   --prop env
        Environment->                   --op env
        (Environment,Environment)
apply f i a prop op = (newProp,op)
                    where newProp = V.unsafeUpd prop [(i,f a (prop V.!i))]


--represents the card succ'
succ'::Field->Slot->Slot
succ' (VInt  n) (Life life,f) = (Life life, VInt $ mkValue (n+1))
succ' _ (Life life,f) = (Life life, VCard I) -- error state V.!

--reprsents the card dbl
dbl::Field->Slot->Slot
dbl (VInt  n) (Life life,f) = (Life life, VInt $ mkValue (2*n))
dbl _ (Life life,f) = (Life life, VCard I) --error state V.!

--reprsents the card revive
revive::Int->Field->Environment->Environment->(Environment,Environment)
revive i (VInt j) prop op = if inEnv j && (not $ isAlive prop j) 
                                then let field' = snd $ prop V.! j
                                         newProp' = V.unsafeUpd newProp [(j,(mkLife 1,field'))]
                                     in (newProp',op)
                                else (newProp,op)
                            where life = fst $ prop V.! i
                                  newProp = V.unsafeUpd prop [(i,(life,VCard I))]
revive i _ prop op = (newProp,op)
                     where life = fst $ prop V.! i
                           newProp = V.unsafeUpd prop [(i,(life,VCard I))] 
                                        
iden::Field->Slot->Slot
iden a slot = (life, a)
              where life = fst slot

--representation of zero
zero :: Field->Slot->Slot  
zero _ (Life life,_) = (Life life,VInt 0)

--representation of put
put:: Field->Slot->Slot
put _ (Life life,_) = (Life life, VCard I)

--reprsentation of dec  isZombie i a prop op
dec::Bool->             -- isZombie?
     Int->              -- index of Slot where dec is applyed to
     Field->            -- the Field of, which the dec is applyed to
     Environment->      -- prop 
     Environment->      -- op
     (Environment,Environment)    
dec True i (VInt j) prop op = if inEnv (255-j)
                                 then let (life,field) = op V.!(255 - j)
                                          life' = getLife life
                                          newLife = if life' > 0 
                                                      then (mkLife $ life'+1,field)
                                                      else (life,field)
                                      in (prop, V.unsafeUpd op [((255-j),newLife)])
                                 else (unsafeUpd prop [(i,(life,VCard I))],op)
                              where life = fst $ prop V.!i
                                                
dec False i (VInt j) prop op  = if inEnv (255-j)
                                 then let (life,field) = op V.!(255 - j)
                                          life' = getLife life
                                          newLife = if life' > 0 
                                                      then (mkLife $ life'-1,field)
                                                      else (life,field)
                                      in (prop, V.unsafeUpd op [((255-j),newLife)])
                                 else (unsafeUpd prop [(i,(life,VCard I))] ,op)
                                where life = fst $ prop V.!i
                                 
dec _ i _ prop op = (V.unsafeUpd prop [(i,newSlot)], op)
                                 where newSlot = (fst $ prop V.!i,VCard I)                                                   
                                                
--reprsentation of inc  isZombie i a prop op
inc::Bool->             -- isZombie?
     Int->              -- index of Slot where dec is applyed to
     Field->            -- the Field of, which the dec is applyed to
     Environment->      -- prop 
     Environment->      -- op
     (Environment,Environment) 
inc False i (VInt j) prop op = if inEnv j 
                                 then let (Life life, field) = prop V.!j
                                          newLife = if life  /= -1
                                                       then (mkLife $ life+1,field)
                                                       else (Life life, field)
                                      in (V.unsafeUpd prop [(j,newLife)], op)
                                 else (unsafeUpd prop [(i,(life,VCard I))] ,op)
                               where life = fst $ prop V.!i                                                                                                  

inc True i (VInt j) prop op = if inEnv j 
                                 then let (Life life, field) = prop V.!j
                                          newLife = if life  /= -1
                                                       then (mkLife $ life-1,field)
                                                       else (Life life, field)
                                      in  (V.unsafeUpd prop [(j,newLife)], op)
                                 else (unsafeUpd prop [(i,(life,VCard I))] ,op)
                              where life = fst $ prop V.!i
                              
inc _ i _ prop op = (V.unsafeUpd prop [(i,(fst $ prop V.!i,VCard I) )], op)


--representation of get i a prop op
get::Int->Field->Environment->Environment->(Environment,Environment)
get i (VInt j) prop op = if inEnv j 
                           then let life = getLife $ fst $ prop V.!i
                                    newSlot = if (isAlive prop j) && (isAlive prop i)
                                                 then (Life life,snd $ prop V.!j)
                                                 else (Life life ,VCard I)
                                in (V.unsafeUpd prop [(i,newSlot)],op)
                           else (prop,op)
get i _ prop op = (unsafeUpd prop [(i,(life,VCard I))], op)
                  where life = fst $ prop V.! i
                  
--representation of copy'
copy'::Int->Field->Environment->Environment->(Environment,Environment)
copy' i (VInt j) prop op = if inEnv j
                              then let Life life = fst $ prop V.!i
                                       (Life lifeOp,fieldOp) = op V.! j
                                       newSlot = (Life life,fieldOp)
                                   in (V.unsafeUpd prop [(j,newSlot)],op)
                              else (prop,op)
copy' i _ prop op = (V.unsafeUpd prop [(i,newSlot)], op)
                   where newSlot = (fst $ prop V.!i,VCard I)

--representation of zombie
zombie:: Int->Field->Environment->Environment->(Environment,Environment)
zombie i (VApp (VInt j) x) prop op = if inEnv j
                                       then let newProp = V.unsafeUpd prop [(i, ( fst $ prop V.!i,VCard I))]
                                                lifeOp = fst $ op V.!(255-j)
                                            in if lifeOp == (Life 0) 
                                                  then (newProp, V.unsafeUpd op [((255-i),(Life (-1),x))])
                                                  else (newProp, op)                          
                                       else (prop,op)
zombie i _ prop op = (V.unsafeUpd prop [(i,(fst $ prop V.!i,VCard I))], op)
                        
--representation of K k x i prop op
k::Field->Int->Environment->Environment->(Environment,Environment)
k x i prop op = (newProp, op)
                where newProp = V.unsafeUpd prop [(i,(fst $ prop V.!i,x))]
                                        
----represent S
--s::Bool->Field->Field->Field->Int->Environment->Environment->Int->(Environment,Environment)
--s isZombie f g x i prop op iter = realEval isZombie i (VApp h y) prop'''' op'' (iter-1)
--                                where slotI = prop V.!i
--                                      (prop', op') = realEval isZombie i (VApp f x) prop op iter
--                                      h = snd $ prop' V.!i
--                                      prop'' =  V.unsafeUpd prop' [(i,slotI)] --} prop' V.// [(i,slotI)]
--                                      (prop''',op'') = realEval isZombie i (VApp g x) prop'' op' iter                       
--                                      y = snd $ prop''' V.!i
--                                      prop'''' =  V.unsafeUpd prop''' [(i,slotI)] --} prop'' V.// [(i,slotI)]


--represent S
s::Bool->Field->Field->Field->Int->Environment->Environment->Int->(Environment,Environment)
s isZombie f g x i prop op iter = realEval isZombie i (VApp h y) prop'' op'' (iter-1)
                                where (prop', op') = realEval isZombie 256 (VApp f x) prop op iter
                                      h = snd $ prop' V.!256
                                      (prop'',op'') = realEval isZombie 257 (VApp g x) prop' op' iter                       
                                      y = snd $ prop'' V.!257
                                      
--representation of Attack
attack::Bool->Field->Field->Field->Int->Environment->Environment->(Environment,Environment)
attack isZombie (VInt ii) (VInt jj) (VInt nn) i prop op
        = if inEnv (255-jj) && inEnv ii
            then let (Life lifeProp,fieldProp) = prop V.!ii
                     (Life lifeOp,fieldOp) = op V.!(255-jj)
                 in if lifeProp > nn --we can attack
                       then if lifeOp > 0 --op's field is not a zombie
                            then let 
                                     prop'= V.unsafeUpd prop [(i,(fst $ prop V.!i, VCard I)),
                                                             (ii,(mkLife $ lifeProp-nn,fieldProp))]
                                     newOpLife = if isZombie 
                                                    then mkLife $ lifeOp+nn*9`div`10
                                                    else mkLife $ lifeOp-nn*9`div`10                            
                                     op' =  V.unsafeUpd op [((255-jj),(newOpLife, fieldOp))]
                                 in (prop',op')
                            else let --op's field is a zombie V.!
                                     prop' = V.unsafeUpd prop [(i,(fst $ prop V.!i, VCard I))]
                                  in (prop',op)
                    else let  --- we can't attack because life < nn
                            prop' = V.unsafeUpd prop [(i,(fst $ prop V.!i, VCard I))]
                         in (prop',op)
            else (prop,op)
attack _ _ _ _ i prop op = (unsafeUpd prop [(i,(life,VCard I))],op)
                              where life = fst $ prop V.! i            

--representation of Help
help::Bool->Field->Field->Field->Int->Environment->Environment->(Environment,Environment)                                   
help isZombie (VInt ii) (VInt jj) (VInt nn) i prop op
    = if inEnv jj && inEnv ii
        then let (Life lifeProp,fieldProp) = prop V.!ii
                 (Life lifeProp2,fieldProp2) = prop V.!jj
             in if lifeProp > nn --we can heal
                 then if ii == jj
                       then let newPropLife = if isZombie
                                               then mkLife $ lifeProp -(nn*11`div`10)-nn
                                               else mkLife $ lifeProp +(nn*11`div`10)-nn
                                prop' = V.unsafeUpd prop [ (i,(fst $ prop V.!i, VCard I)),
                                                           (ii,(newPropLife,fieldProp))]
                            in (prop',op)
                       else if lifeProp2 > 0 --props2's field is not a zombie
                             then let
                                newProp2Life = if isZombie 
                                                then mkLife $ lifeProp2-(nn*11`div`10)
                                                else mkLife $ lifeProp2+(nn*9`div`10)                                                                        
                                prop'= V.unsafeUpd prop [ (i,(fst $ prop V.!i, VCard I)),
                                                           (ii,(mkLife $ lifeProp-nn,fieldProp)),
                                                           (jj,(newProp2Life,fieldProp2)) ]   
                              in (prop',op)
                             else let --props2's field is a zombie V.!
                                prop' = V.unsafeUpd prop [(i,(fst $ prop V.!i, VCard I))]
                              in (prop',op)
                 else let  --- we can't help because life < nn
                      prop' = V.unsafeUpd prop [(i,(fst $ prop V.!i, VCard I))]
                  in (prop',op) 
        else (prop,op)
help _ _ _ _ i prop op = (unsafeUpd prop [(i,(life,VCard I))],op)
                           where life = fst $ prop V.! i         
                                 
                        
                        
                             
